package game.square;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import game.dungeon.position.Direction;
import game.square.temperature.Temperature;
import game.square.temperature.TemperatureScale;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.junit.Before;
import org.junit.Test;

public class TeleportationSquareTest {

	private TeleportationSquare squareTemp50Cel, roomSquareCenter, roomSquareWestOpen,roomSquareWestWestOpen,roomSquareNorthOpen,roomSquareSouth, roomSquareSouthSouth;
	private BigDecimal bigDecimal10;
	private BigDecimal bigDecimal100;
	
	
	@Before
	public void setUp() throws Exception {
		bigDecimal10 = new BigDecimal(BigInteger.valueOf(1000),2 );
		squareTemp50Cel = new CommonSquare(50,TemperatureScale.CELCIUS, bigDecimal10, false, null, new ArrayList<Square>());
		bigDecimal100 = new BigDecimal(BigInteger.valueOf(10000),2);
		initializeRoom();
	}
	
	private void initializeRoom() {
		roomSquareCenter = new CommonSquare();
		roomSquareCenter.addBorderInDirection(new Door(), Direction.WEST);
		roomSquareCenter.addBorderInDirection(new Door(), Direction.SOUTH);
		roomSquareWestOpen = new CommonSquare();
		roomSquareWestOpen.addBorderInDirection(new Door(), Direction.WEST);
		roomSquareCenter.addNeighborInDirection(roomSquareWestOpen, Direction.WEST);
		roomSquareWestWestOpen = new CommonSquare();
		roomSquareWestOpen.addNeighborInDirection(roomSquareWestWestOpen, Direction.WEST);
		roomSquareNorthOpen = new CommonSquare();
		roomSquareCenter.addNeighborInDirection(roomSquareNorthOpen, Direction.NORTH);
		//the room of this square stays closed, so it doesn't
		// belong to the current room.
		roomSquareSouth = new CommonSquare();
		roomSquareSouthSouth = new CommonSquare();
		roomSquareSouth.addNeighborInDirection(roomSquareSouthSouth,Direction.SOUTH);
		roomSquareCenter.addNeighborInDirection(roomSquareSouth, Direction.SOUTH);
		((Door) roomSquareCenter.getBorderInDirection(Direction.WEST)).open();
		((Door) roomSquareCenter.getBorderInDirection(Direction.NORTH)).open();
		((Door) roomSquareWestOpen.getBorderInDirection(Direction.WEST)).open();

		
	}
	
	@Test
	public final void setHumidity_LegalCase(){
		squareTemp50Cel.setHumidity(bigDecimal100);
		assertEquals(bigDecimal100,squareTemp50Cel.getHumidity());
		squareTemp50Cel.setHumidity(new BigDecimal(BigInteger.valueOf(0),2));
		assertEquals(new BigDecimal(BigInteger.valueOf(0),2),squareTemp50Cel.getHumidity());
	}
	
	@Test (expected = AssertionError.class)
	public final void setHumidity_LowerIllegalCase(){
		squareTemp50Cel.setHumidity(new BigDecimal(BigInteger.valueOf(-1),2));
	}
	
	@Test (expected = AssertionError.class)
	public final void setHumidity_UpperIllegalCase(){
		squareTemp50Cel.setHumidity(new BigDecimal(BigInteger.valueOf(1001),1));
	}
	
	@Test (expected = AssertionError.class)
	public final void setHumidity_PrecisionIllegalCase(){
		squareTemp50Cel.setHumidity(new BigDecimal(BigInteger.valueOf(00),1));
	}
	
	@Test
	public final void setRoomEquilibrium_SameTemperatureScales(){
		roomSquareCenter.setTemperature(new Temperature(50, -200, 50000, TemperatureScale.CELCIUS));
		roomSquareCenter.setHumidity(bigDecimal100);
		checkSquaresForEquilibrium(27,BigDecimal.valueOf(3625, 2));
	}
	
	@Test
	public final void setRoomEquilibrium_OpenDoor(){
		roomSquareSouth.setTemperature(new Temperature(50, -200, 50000, TemperatureScale.CELCIUS));
		roomSquareSouth.setHumidity(bigDecimal100);
		((Door)roomSquareCenter.getBorderInDirection(Direction.SOUTH)).open();
		checkSquaresForEquilibrium(26, BigDecimal.valueOf(3200,2));
	}
	
	@Test 
	public final void getRoom_SingleCase(){
		Set<Square> roomSquares = new HashSet<Square>();
		roomSquares.add(roomSquareCenter);
		roomSquares.add(roomSquareNorthOpen);
		roomSquares.add(roomSquareWestOpen);
		roomSquares.add(roomSquareWestWestOpen);
		assertTrue(roomSquareCenter.getRoom().containsAll(roomSquares)
				&& roomSquareCenter.getRoom().size() == roomSquares.size());
	}
	
	private void checkSquaresForEquilibrium(int expectedTemperature, BigDecimal expectedHumidity) {
		Iterator<? extends Square> iterator = roomSquareCenter.getRoom().iterator();
		while (iterator.hasNext()){
			Square roomSquare = iterator.next();
			assertEquals(expectedTemperature,roomSquare
					.getTemperature().getTemperatureValue(TemperatureScale.CELCIUS));
			assertEquals(expectedHumidity,roomSquare.getHumidity());
		}
	}
	
	@Test
	public final void canReachSquare_ReachableWithoutTeleportation_TrivialCase(){
		assertTrue(roomSquareCenter.canReachSquare(roomSquareWestOpen));
	}
	@Test
	public final void canReachSquare_ReachableWithoutTeleportation_RecursiveCase(){
		assertTrue(roomSquareCenter.canReachSquare(roomSquareWestWestOpen));
	}
	
	@Test
	public final void canReachSquare_ReachableWithTeleportationOnly_TrivialCase(){
		roomSquareCenter.addTeleportationDestination(roomSquareSouth);
		assertTrue(roomSquareCenter.canReachSquare(roomSquareSouth));
		assertFalse(roomSquareSouth.canReachSquare(roomSquareCenter));
	}
	
	@Test
	public final void canReachSquare_ReachableWithTeleportationOnly_RecursiveCase(){
		roomSquareCenter.addTeleportationDestination(roomSquareSouth);
		roomSquareSouth.addTeleportationDestination(roomSquareSouthSouth);
		assertTrue(roomSquareCenter.canReachSquare(roomSquareSouthSouth));
		assertFalse(roomSquareSouthSouth.canReachSquare(roomSquareCenter));
	}
	
	@Test
	public final void canReachSquare_UnreachableSquare(){
		assertFalse(roomSquareCenter.canReachSquare(roomSquareSouth));
		assertFalse(roomSquareSouth.canReachSquare(roomSquareSouthSouth));

	}
	
	

}
